﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace SilverChart.Core.Service {
    /// <summary>
    /// This class is a Core Service. It supply services register service.
    /// This class is a Singleton.
    /// </summary>
    public class ServiceManager {
        private static ServiceManager _instance;
        private static Object _instanceLock = new Object();

        private IDictionary<Type, IList<ServiceWrapper>> registedServcies;

        public static ServiceManager Instance {
            get {
                if (_instance == null) {
                    lock (_instanceLock) {
                        if (_instance == null) {
                            _instance = new ServiceManager();
                        }
                    }
                }

                return _instance;
            }
        }

        private ServiceManager() {
            registedServcies = new Dictionary<Type, IList<ServiceWrapper>>();
        }

        internal void RegisteServcie(Type serviceProviderType, ServiceProviderAttribute attribute) {
            IList<Type> servicePoints = FindServicePoints(serviceProviderType);
            foreach (Type point in servicePoints) {
                ServiceWrapper wrapper = new ServiceWrapper(point, serviceProviderType, attribute);
                GetServiceProviders(point).Add(wrapper);
            }
        }

        private IList<Type> FindServicePoints(Type serviceProviderType) {
            IList<Type> points = new List<Type>();
            Type[] types = serviceProviderType.GetInterfaces();
            foreach (Type type in types) {
                ServicePointAttribute attribute = (ServicePointAttribute)Attribute.GetCustomAttribute(type, typeof(ServicePointAttribute));
                if (attribute != null) {
                    points.Add(type);
                }
            }
            return points;
        }

        internal void AddServicePoint(Type type) {
            if (!registedServcies.ContainsKey(type)) {
                registedServcies.Add(type, new List<ServiceWrapper>());
            }
        }

        IList<ServiceWrapper> GetServiceProviders(Type type) {
            IList<ServiceWrapper> providers = null;
            if (!registedServcies.TryGetValue(type, out providers)) {
                AddServicePoint(type);
                providers = registedServcies[type];
            }
            return providers;
        }

        /// <summary>
        /// Return all implementations of service type T
        /// </summary>
        /// <typeparam name="T">The type which service implemented</typeparam>
        /// <returns>All service type T implementations</returns>
        public IList<T> GetServices<T>() {
            List<T> services = new List<T>();
            IList<ServiceWrapper> providers = null;
            if (registedServcies.TryGetValue(typeof(T), out providers)) {
                foreach (ServiceWrapper wrapper in providers) {
                    services.Add(wrapper.GetProviderInstance<T>());
                }
            }
            return services.AsReadOnly();
        }

        public IList<Type> GetServiceTypes<T>() {
            List<Type> services = new List<Type>();
            IList<ServiceWrapper> providers = null;
            if (registedServcies.TryGetValue(typeof(T), out providers)) {
                foreach (ServiceWrapper wrapper in providers) {
                    services.Add(wrapper.Provider);
                }
            }
            return services.AsReadOnly();
        }

        /// <summary>
        /// Return a implementation of service type T
        /// </summary>
        /// <typeparam name="T">The type which service implemented</typeparam>
        /// <returns>The first implementation of service type T</returns>
        public T GetService<T>() {
            IList<ServiceWrapper> providers = null;
            if (registedServcies.TryGetValue(typeof(T), out providers)) {
                if (providers.Count > 0) {
                    return providers[0].GetProviderInstance<T>();
                }
            }
            return default(T);
        }

        /// <summary>
        /// Return a implementation of service type T, which has a given name
        /// </summary>
        /// <typeparam name="T">The type which service implemented</typeparam>
        /// <param name="servcieName">service implementation name</param>
        /// <returns>The first implementation of service type T</returns>
        public T GetService<T>(String servcieName) {
            if (servcieName == null) {
                return GetService<T>();
            }

            IList<ServiceWrapper> providers = null;
            if (registedServcies.TryGetValue(typeof(T), out providers)) {
                foreach (ServiceWrapper wrapper in providers) {
                    if (servcieName == wrapper.ProviderAttribute.ServiceName) {
                        return wrapper.GetProviderInstance<T>();
                    }
                }
            }
            return default(T);
        }

        #region ToString override
        /// <summary>
        /// Return all registed service in a string
        /// </summary>
        /// <returns></returns>
        public override String ToString() {
            StringBuilder sb = new StringBuilder();

            foreach (Type type in registedServcies.Keys) {
                sb.Append("I:");
                sb.Append(type.FullName);
                sb.Append(":\n");
                GetServiceProvidersString(sb, registedServcies[type], "\t");
            }
            return sb.ToString();
        }

        private void GetServiceProvidersString(StringBuilder sb, IList<ServiceWrapper> serviceProviders, String indent) {
            foreach (ServiceWrapper wrapper in serviceProviders) {
                sb.Append(indent);
                sb.Append(wrapper.ToString());
                sb.Append("\n");
            }
        }
        #endregion

        private class ServiceWrapper {
            public Type ServicePoint { get; private set; }
            public Type Provider { get; private set; }
            public ServiceProviderAttribute ProviderAttribute { get; private set; }
            private Object instance;

            public T GetProviderInstance<T>() {
                if (!typeof(T).IsAssignableFrom(Provider)) {
                    throw new InvalidCastException(Provider.FullName + " cannot be cast to " + typeof(T).FullName);
                }

                if (instance != null) {
                    return (T)instance;
                }

                Object providerInstance = CreateProvider();

                if (ProviderAttribute.Singleton) {
                    instance = providerInstance;
                }
                return (T)providerInstance;
            }

            public ServiceWrapper(Type servicePoint, Type providerType, ServiceProviderAttribute attribute) {
                ServicePoint = servicePoint;
                Provider = providerType;
                ProviderAttribute = attribute;
                if (!attribute.LayzeCreation) {
                    if (attribute.Singleton) {
                        instance = CreateProvider();
                    }
                }
            }

            private Object CreateProvider() {
                Object o = null;
                try {
                    o = Activator.CreateInstance(Provider);
                }
                catch {
                }

                return o;
            }

            public override string ToString() {
                String str = Provider.FullName;
                if (instance != null) {
                    str += ", O";
                }

                if (ProviderAttribute.LayzeCreation) {
                    str += ", L";
                }

                if (ProviderAttribute.Singleton) {
                    str += ", S";
                }

                if (ProviderAttribute.ServiceName != null && ProviderAttribute.ServiceName.Trim().Length > 0) {
                    str += ", [" + ProviderAttribute.ServiceName + "]";
                }

                return str;
            }
        }
    }
}
